package org.example.demo;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import lombok.extern.java.Log;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.hyperledger.fabric.contract.Context;
import org.hyperledger.fabric.contract.ContractInterface;
import org.hyperledger.fabric.contract.annotation.*;
import org.hyperledger.fabric.shim.ChaincodeException;
import org.hyperledger.fabric.shim.ChaincodeStub;
import org.hyperledger.fabric.shim.ledger.KeyValue;
import org.hyperledger.fabric.shim.ledger.QueryResultsIterator;
import org.hyperledger.fabric.shim.ledger.QueryResultsIteratorWithMetadata;

import java.util.List;
import java.util.Random;
import java.util.logging.Level;

/**
 * @Author: kingtao
 * @Date: 2023-04-21-19:32
 * @Description: Cat相关接口
 */

@Contract(
        name = "Cat",
        info = @Info(
                title = "Cat contract",
                description = "The hyperlegendary car contract",
                version = "0.0.1-SNAPSHOT",
                license = @License(
                        name = "Apache 2.0 License",
                        url = "http://www.apache.org/licenses/LICENSE-2.0.html"),
                contact = @Contact(
                        email = "f.carr@example.com",
                        name = "F Carr",
                        url = "https://hyperledger.example.com")))
@Default
@Log
public class CatContract implements ContractInterface {

    @Transaction
    public void initLedger(final Context ctx) {
        ChaincodeStub stub = ctx.getStub();
        for (int i = 0; i < 10; i++) {
            Cat cat = new Cat().setName("cat-" + i)
                    .setAge(new Random().nextInt())
                    .setBreed("橘猫")
                    .setColor("橘黄色");
            stub.putStringState(cat.getName(), JSON.toJSONString(cat));
        }
    }

    @Transaction
    public Cat queryCat(final Context ctx, final String key) {
        ChaincodeStub stub = ctx.getStub();
        String catState = stub.getStringState(key);

        if (StringUtils.isBlank(catState)) {
            String error = String.format("姓名为 %s 的猫不存在", key);
            System.out.println(error);
            throw new ClassCastException(error);
        }

        return JSON.parseObject(catState, Cat.class);
    }

    @Transaction
    private QueryCatList queryCatList(ChaincodeStub stub, String query) {

        QueryCatList list = new QueryCatList();
        QueryResultsIterator<KeyValue> queryResult = stub.getQueryResult(query);
        List<QueryCat> res = Lists.newArrayList();

        if ( !IterableUtils.isEmpty(queryResult)) {
            for (KeyValue kv : queryResult) {
                res.add(new QueryCat().setKey(kv.getKey()).setCat(JSON.parseObject(kv.getStringValue(), Cat.class)));
            }
            list.setCats(res);
        }
        return list;
    }

    @Transaction
    public QueryCatList queryCatByName(final Context ctx, String name) {

        ChaincodeStub stub = ctx.getStub();

        log.info(String.format("使用 name 查询 cat：，name = %s", name));

        String query = String.format("{\"selector\":{\"name\":\"%s\"} , \"use_index\":[\"_design/indexNameColorDoc\", \"indexNameColor\"]}", name);

        return queryCatList(ctx.getStub(), query);
    }

    @Transaction
    private QueryCatList queryCatListByNameAndColor(final Context ctx, String name, String color) {
        log.info(String.format("使用 name & color 查询 cat , name = %s , color = %s" , name , color));

        String query = String.format("{\"selector\":{\"name\":\"%s\"} , \"use_index\":[\"_design/indexNameColorDoc\", \"indexNameColor\"]}", name);

        return queryCatList(ctx.getStub() , query);
    }

    @Transaction
    public QueryCatPage queryCatPageByName(final Context ctx, String name, Integer pageSize, String bookmark) {
        log.info(String.format("使用 name 进行 分页查询cat，name = %s", name));

        String query = String.format("{\"selector\":{\"name\":\"%s\"} , \"use_index\":[\"_design/indexNameColorDoc\", \"indexNameColor\"]}", name);

        log.info(String.format("query string = %s", query));

        ChaincodeStub stub = ctx.getStub();

        QueryResultsIteratorWithMetadata<KeyValue> queryResult = stub.getQueryResultWithPagination(query, pageSize, bookmark);

        List<QueryCat> cats = Lists.newArrayList();

        if ( !IterableUtils.isEmpty(queryResult)) {
            for (KeyValue keyValue : queryResult) {
                cats.add(new QueryCat().setKey(keyValue.getKey()).setCat(JSON.parseObject(keyValue.getStringValue(), Cat.class)));
            }
        }
        return new QueryCatPage().setCats(cats).setBookmark(queryResult.getMetadata().getBookmark());
    }

    @Transaction
    public Cat createCat(final Context ctx, final String key, String name, Integer age, String color, String breed) {
        ChaincodeStub stub = ctx.getStub();
        String catState = stub.getStringState(key);

        if (StringUtils.isNotBlank(catState)) {
            String error = String.format("姓名为 %s 的猫存在", key);
            System.out.println(error);
            throw new ClassCastException(error);
        }

        Cat cat = new Cat();
        cat.setAge(age);
        cat.setName(name);
        cat.setBreed(breed);
        cat.setColor(color);
        stub.putStringState(key, JSON.toJSONString(cat));

        return cat;
    }

    @Transaction
    public Cat updateCat(final Context ctx, final String key, String name, Integer age, String color, String breed) {
        ChaincodeStub stub = ctx.getStub();
        String catState = stub.getStringState(key);
        if (StringUtils.isBlank(catState)) {
            String error = String.format("姓名为 cat-%s 的猫不存在", key);
            System.out.println(error);
            throw new ClassCastException(error);
        }
        Cat cat = new Cat();
        cat.setAge(age);
        cat.setName(name);
        cat.setBreed(breed);
        cat.setColor(color);
        stub.putStringState(key, JSON.toJSONString(cat));

        return cat;
    }

    @Transaction
    public Cat deleteCat(final Context ctx, final String key) {
        ChaincodeStub stub = ctx.getStub();
        String catState = stub.getStringState(key);
        if (StringUtils.isBlank(catState)) {
            String error = String.format("姓名为 cat-%s 的猫不存在", key);
            System.out.println(error);
            throw new ClassCastException(error);
        }
        stub.delState(key);
        return JSON.parseObject(catState, Cat.class);
    }

    @Transaction
    public byte[] queryPrivateCatHash(final Context ctx, final String key) {
        ChaincodeStub stub = ctx.getStub();

        String collection = getCollectionName(stub);
        byte[] hash = stub.getPrivateDataHash(collection, key);

        if (ArrayUtils.isEmpty(hash)) {
            String err = String.format("Private Cat %s does not exist", key);
            log.log(Level.WARNING, err);
            throw new ClassCastException(err);
        }

        return hash;
    }

    @Transaction
    public PrivateCat queryPrivateCat(final Context ctx, final String collection, final String key) {
        ChaincodeStub stub = ctx.getStub();

        log.info(String.format("查询到私有数据, collection [$s] key [%s], mspId [%s]", collection, stub.getMspId(), key));

        String catState = stub.getPrivateDataUTF8(collection, key);

        if (StringUtils.isBlank(catState)) {
            String err = String.format("Private cat %s does not exist", key);
            log.log(Level.WARNING, err);
            throw new ChaincodeException(err);
        }
        return JSON.parseObject(catState, PrivateCat.class);
    }

    @Transaction
    public PrivateCat createPrivateCat(final Context ctx, final String collection, final String key, String name, Integer age, String color, String breed) {
        ChaincodeStub stub = ctx.getStub();
        log.info(String.format("创建私有数据， collection [%s], mspId [%s], key [%s] name [%s] age [%s]  color [%s] breed [%s]", collection, stub.getMspId(), key, name, age, color, breed));

        String catState = stub.getPrivateDataUTF8(collection, key);

        if (StringUtils.isNotBlank(catState)) {
            String err = String.format("Private Cat %s 已经存在");
            log.log(Level.WARNING, err);
            throw new ChaincodeException(err);
        }

        PrivateCat privateCat = new PrivateCat();
        Cat cat = new Cat();
        cat.setColor(color).setName(name).setBreed(breed).setAge(age);
        privateCat.setCat(cat).setCollection(collection);

        String json = JSON.toJSONString(privateCat);
        log.info(String.format("要保存的数据 %s", json));

        stub.putPrivateData(collection, key, json);

        return privateCat;
    }

    @Transaction
    public PrivateCat updatePrivateCat(final Context ctx, final String collection, final String key, String name, Integer age, String color, String breed) {
        ChaincodeStub stub = ctx.getStub();
        log.info(String.format("创建私有数据， collection [%s], mspId [%s], key [%s] name [%s] age [%s]  color [%s] breed [%s]", collection, stub.getMspId(), key, name, age, color, breed));

        String catState = stub.getPrivateDataUTF8(collection, key);

        if (StringUtils.isBlank(catState)) {
            String err = String.format("Private Cat %s 不存在，无法更新");
            log.log(Level.WARNING, err);
            throw new ChaincodeException(err);
        }

        PrivateCat privateCat = new PrivateCat();
        Cat cat = new Cat();
        cat.setColor(color).setName(name).setBreed(breed).setAge(age);
        privateCat.setCat(cat).setCollection(collection);

        String json = JSON.toJSONString(privateCat);
        log.info(String.format("要保存的数据 %s", json));

        stub.putPrivateData(collection, key, json);

        return privateCat;
    }

    @Transaction
    public PrivateCat deletePrivateCat(final Context ctx, final String collection, final String key) {
        ChaincodeStub stub = ctx.getStub();

        log.info(String.format("删除私有数据 , collection [%s] , mspId [%s] , key [%s] ", collection, stub.getMspId(), key));

        String catState = stub.getPrivateDataUTF8(collection, key);

        if (StringUtils.isBlank(catState)) {
            String err = String.format("Private Cat %s 不存在");
            log.log(Level.WARNING, err);
            throw new ChaincodeException(err);
        }

        stub.delPrivateData(collection, key);
        return JSON.parseObject(catState, PrivateCat.class);
    }

    @Transaction
    public String getCollectionName(final ChaincodeStub stub) {
        String name = "collection" + stub.getMspId() + "Cats";
        log.info(String.format("私有数据集合名称：[%s]", name));
        return name;
    }

    @Override
    public void beforeTransaction(Context ctx) {
        log.info("*************************************** beforeTransaction ***************************************");
    }

    @Override
    public void afterTransaction(Context ctx, Object result) {
        log.info("*************************************** afterTransaction ***************************************");
        System.out.println("result --------> " + result);
    }
}
